﻿using System.Linq.Expressions;
using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Primitives;

namespace Devonline.AspNetCore;

/// <summary>
/// 公共数据处理服务
/// </summary>
/// <typeparam name="TDbContext">数据库上下文</typeparam>
/// <typeparam name="TEntitySet">业务数据类型</typeparam>
/// <typeparam name="TKey">主键类型</typeparam>
public interface IDataService<TDbContext, TEntitySet, TKey>
    where TDbContext : DbContext
    where TEntitySet : class, IEntitySet<TKey>
    where TKey : IConvertible
{
    #region 基础属性和方法, 业务无关属性和方法
    /// <summary>
    /// 当前对象是否启用了缓存
    /// </summary>
    bool EnableCache { get; }
    /// <summary>
    /// 当前登录的用户名
    /// </summary>
    string UserName { get; }
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识在 User.Claims 中的 type 为 sub; 在 request 中为 userId
    /// </summary>
    /// <returns></returns>
    string? GetUserId();
    /// <summary>
    /// 从 httpContext 获取用户名, 用户名在 User.Claims 中的 type 为 UserName;
    /// </summary>
    /// <returns></returns>
    string? GetUserName();
    /// <summary>
    /// 从当前 request 中获取参数的值, 取值顺序 Query -> Form -> Header -> Cookie
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    T? GetRequestOption<T>(string key) where T : IConvertible;
    /// <summary>
    /// 获取上下文对象中参数/变量的值, 取值顺序 HttpContext -> Query -> Form -> Header -> Cookie
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    T? GetContextOption<T>(string key) where T : IConvertible;
    /// <summary>
    /// 获取查询选项列表
    /// </summary>
    /// <returns>返回合并后的查询选项</returns>
    IEnumerable<KeyValuePair<string, StringValues>> GetRequestOptions();
    /// <summary>
    /// 获取附件绝对地址
    /// </summary>
    /// <param name="fileName">附件文件名</param>
    /// <returns></returns>
    string GetAttachmentPath(string fileName);
    /// <summary>
    /// 获取随机的临时文件名
    /// </summary>
    /// <param name="extension"></param>
    /// <returns></returns>
    string GetTempFileName(string? extension = default);
    /// <summary>
    /// 从分布式缓存获取值
    /// </summary>
    /// <typeparam name="TValue">缓存值类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <returns></returns>
    Task<TValue?> GetCacheAsync<TValue>(string key);
    /// <summary>
    /// 将值写入分布式缓存, 带预设过期时间
    /// </summary>
    /// <typeparam name="TValue">缓存值类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="value">缓存值</param>
    Task SetCacheAsync<TValue>(string key, TValue value);
    /// <summary>
    /// 刷新缓存
    /// </summary>
    /// <returns></returns>
    Task RefreshCacheAsync();
    /// <summary>
    /// 提交当前操作
    /// </summary>
    /// <returns></returns>
    Task<int> SaveChangesAsync();
    #endregion

    #region 基础数据操作方法, 面向当前业务类型
    /// <summary>
    /// 获取 TypeAdapterConfig 默认实例
    /// </summary>
    /// <returns></returns>
    TypeAdapterConfig GetAdapterConfig();
    /// <summary>
    /// 从当前上下文解除当前对象的跟踪状态
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    void Detach(TEntitySet entitySet);
    /// <summary>
    /// 调用实例的 Create 方法为基础字段赋值
    /// </summary>
    /// <param name="entitySet"></param>
    void Create(TEntitySet entitySet);
    /// <summary>
    /// 调用实例的 Update 方法为基础字段赋值
    /// </summary>
    /// <param name="entitySet"></param>
    void Update(TEntitySet entitySet);
    /// <summary>
    /// 逻辑修改, 逻辑修改操作会在源数据基础上产生新的记录作为历史记录
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    void Update(TEntitySet entitySet, bool isLogical = false);
    /// <summary>
    /// 逻辑删除, 逻辑删除操作实际上只是将数据状态修改为已删除状态, 并不会真的删除数据
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    void Delete(TEntitySet entitySet, bool isLogical = false);
    #endregion

    #region 基础数据操作方法, 面向非当前业务类型
    /// <summary>
    /// 获取 TypeAdapterConfig 默认实例
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <returns></returns>
    TypeAdapterConfig GetAdapterConfig<TModel>() where TModel : class, IEntitySet<TKey>;
    /// <summary>
    /// 从当前上下文解除当前对象的跟踪状态
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    void Detach<TModel>(TModel model) where TModel : class, IEntitySet<TKey>;
    /// <summary>
    /// 调用实例的 Create 方法为基础字段赋值
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    void Create<TModel>(TModel model) where TModel : class, IEntitySet<TKey>;
    /// <summary>
    /// 调用实例的 Update 方法为基础字段赋值
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    void Update<TModel>(TModel model) where TModel : class, IEntitySet<TKey>;
    /// <summary>
    /// 逻辑修改, 逻辑修改操作会在源数据基础上产生新的记录作为历史记录
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    void Update<TModel>(TModel model, bool isLogical = false) where TModel : class, IEntitySet<TKey>;
    /// <summary>
    /// 逻辑删除, 逻辑删除操作实际上只是将数据状态修改为已删除状态, 并不会真的删除数据
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    void Delete<TModel>(TModel model, bool isLogical = false) where TModel : class, IEntitySet<TKey>;
    #endregion

    #region 数据查询方法, 面向当前业务类型
    /// <summary>
    /// 统一数据源查询入口, 可缓存数据源, 仅返回当前类型引用
    /// </summary>
    /// <returns></returns>
    IQueryable<TEntitySet> GetQueryable();
    /// <summary>
    /// 统一数据源查询入口, 可缓存数据源, 仅返回当前类型引用
    /// </summary>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    IQueryable<TEntitySet> GetQueryable(Expression<Func<TEntitySet, bool>> predicate);
    /// <summary>
    /// 根据 id 获取对象
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    Task<TEntitySet?> GetAsync(TKey id);
    /// <summary>
    /// 从 QueryOptions 和上下文自动获取查询表达式的内容并执行查询返回分页的结果
    /// 暂不支持 orderby, select 和 expand 表达式
    /// </summary>
    /// <returns></returns>
    Task<PagedResult> GetPagedResultAsync();
    /// <summary>
    /// 适用于新增时判断对象是否存在, 存在则抛出异常
    /// </summary>
    /// <param name="id">业务数据编号</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    Task ThrowIfExistAsync(TKey? id);
    /// <summary>
    /// 适用于更新时判断当前对象是否存在, 存在则返回按 Id 查询的结果, 不存在则抛出异常
    /// </summary>
    /// <param name="id">业务数据主键</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    Task<TEntitySet> GetIfExistAsync(TKey id);
    /// <summary>
    /// 获取业务数据的集合成员, 成员是附加信息, 附件及特定类型的附属集合
    /// </summary>
    /// <typeparam name="TElement">附加信息, 附件或附属集合的类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <returns></returns>
    IEnumerable<TElement>? GetCollectionMembers<TElement>(TEntitySet entitySet) where TElement : class, IEntitySet<TKey>;
    #endregion

    #region 数据查询方法, 面向非当前业务类型
    /// <summary>
    /// 统一数据源查询入口, 可缓存数据源, 仅返回当前类型引用
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <returns></returns>
    IQueryable<TModel> GetQueryable<TModel>() where TModel : class;
    /// <summary>
    /// 统一数据源查询入口, 可缓存数据源, 仅返回当前类型引用
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    IQueryable<TModel> GetQueryable<TModel>(Expression<Func<TModel, bool>> predicate) where TModel : class;
    /// <summary>
    /// 根据 id 获取对象
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="id"></param>
    /// <returns></returns>
    Task<TModel?> GetAsync<TModel>(TKey id) where TModel : class, IEntitySet<TKey>;
    /// <summary>
    /// 从 QueryOptions 和上下文自动获取查询表达式的内容并执行查询返回分页的结果
    /// 暂不支持 orderby, select 和 expand 表达式
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <returns></returns>
    Task<PagedResult> GetPagedResultAsync<TModel>() where TModel : class, IEntitySet<TKey>;
    /// <summary>
    /// 适用于新增时判断对象是否存在, 存在则抛出异常
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="id">业务数据编号</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    Task ThrowIfExistAsync<TModel>(TKey? id) where TModel : class, IEntitySet<TKey>;
    /// <summary>
    /// 适用于更新时判断当前对象是否存在, 存在则返回按 Id 查询的结果, 不存在则抛出异常
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="id">业务数据主键</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    Task<TModel> GetIfExistAsync<TModel>(TKey id) where TModel : class, IEntitySet<TKey>;
    /// <summary>
    /// 获取业务数据的集合成员, 成员是附加信息, 附件及特定类型的附属集合
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <typeparam name="TElement">附加信息, 附件或附属集合的类型</typeparam>
    /// <param name="model">业务数据</param>
    /// <returns></returns>
    IEnumerable<TElement>? GetCollectionMembers<TModel, TElement>(TModel model) where TModel : class, IEntitySet<TKey> where TElement : class, IEntitySet<TKey>;
    #endregion

    #region 数据操作方法, 面向当前业务类型
    /// <summary>
    /// 新增阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    Task UniqueAsync(TEntitySet entitySet);
    /// <summary>
    /// 单个数据对象的新增, 自动验证重复
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task<TEntitySet> AddAsync(TEntitySet entitySet, DataServiceContext? context = default);
    /// <summary>
    /// 更新阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task<TEntitySet> UpdateAsync(TEntitySet entitySet, DataServiceContext? context = default);
    /// <summary>
    /// 删除记录
    /// </summary>
    /// <param name="id">业务数据主键</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task DeleteAsync(TKey id, DataServiceContext? context = default);
    #endregion

    #region 数据操作方法, 面向非当前业务类型
    /// <summary>
    /// 新增阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    Task UniqueAsync<TModel>(TModel model) where TModel : class, IEntitySet<TKey>;
    /// <summary>
    /// 单个数据对象的新增, 自动验证重复
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task<TModel> AddAsync<TModel>(TModel model, DataServiceContext? context = default) where TModel : class, IEntitySet<TKey>;
    /// <summary>
    /// 更新阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task<TModel> UpdateAsync<TModel>(TModel model, DataServiceContext? context = default) where TModel : class, IEntitySet<TKey>;
    /// <summary>
    /// 删除记录
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="id">业务数据主键</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    Task DeleteAsync<TModel>(TKey id, DataServiceContext? context = default) where TModel : class, IEntitySet<TKey>;
    #endregion
}

/// <summary>
/// 字符串作为主键的默认实现接口
/// </summary>
/// <typeparam name="TDbContext">数据库上下文</typeparam>
/// <typeparam name="TEntitySet">业务数据类型</typeparam>
public interface IDataService<TDbContext, TEntitySet> :
    IDataService<TDbContext, TEntitySet, string>
    where TDbContext : DbContext
    where TEntitySet : class, IEntitySet<string>
{ }